package com.netty.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

import com.netty.common.core.annotion.RpcService;
import com.netty.common.protocol.RpcDecoder;
import com.netty.common.protocol.RpcEncoder;
import com.netty.common.protocol.RpcRequest;
import com.netty.common.protocol.RpcResponse;
import com.netty.server.handler.ServerHandler;
import com.netty.server.registry.ServiceRegistry;

public class Server implements  BeanPostProcessor {
	/**
	 * 服务地址
	 */
	private String serverAddress;
	/**
	 * 注册服务
	 */
	private ServiceRegistry serviceRegistry;
	
	private static ThreadPoolExecutor threadPoolExecutor;
	
	public Server(String serverAddress, ServiceRegistry serviceRegistry){
		this.serverAddress = serverAddress;
		this.serviceRegistry = serviceRegistry;
	}
	
	/**
	 * 服务名称
	 */
	private Map<String, Object> handlerMap = new HashMap<>();
	/**
	 * 主线程池
	 */
	private EventLoopGroup bossGroup = null;
	/**
	 * 工作线程池
	 */
	private EventLoopGroup workerGroup = null;
	/**
	 * bean初始化后调用
	 */
	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName)
			throws BeansException {
		Class<? extends Object> clazz = bean.getClass();
		RpcService rpcService = clazz.getAnnotation(RpcService.class);
		if (rpcService != null) {
			Class<?> class1 = rpcService.value();
			String interfaceName = class1.getName();
			System.out.println(interfaceName);
			handlerMap.put(interfaceName, bean);
		}
		return bean;
	}
	
	public void start() throws Exception{
		if (bossGroup == null && workerGroup == null) {
			bossGroup = new NioEventLoopGroup();
			workerGroup = new NioEventLoopGroup();
			ServerBootstrap bootstrap = new ServerBootstrap();
			bootstrap.group(bossGroup, workerGroup);
			/**
			 * NioServerSocketChannel类初始化channel用来接收客户端请求
			 * 设置socket工厂
			 */
			bootstrap.channel(NioServerSocketChannel.class)
			//设置TCP参数
			//https://www.cnblogs.com/549294286/p/7112030.html
			.option(ChannelOption.SO_BACKLOG, 128)//serverSocketChannel的设置 100个连接连接缓冲池大小
			.childOption(ChannelOption.SO_KEEPALIVE, true)//socketChannel设置，检查连接，清除长时间不使用的连接
			.childOption(ChannelOption.TCP_NODELAY, true)//socketChannel设置，关闭延迟发送
			.handler(new LoggingHandler(LogLevel.INFO))
			/**
			 * 通常会为新的SocketChannel添加一些handler来设置ChannelPipeline
			 * ChannelInitializer是一个特殊的handler，其中initChannel方法可以为SocketChannel
			 * 的pipeline添加指定的hanlder
			 * 设置管道工厂
			 */
			.childHandler(new ChannelInitializer<SocketChannel>() {

				@Override
				protected void initChannel(SocketChannel ch) throws Exception {
					ch.pipeline()
					 /** 
     *  
     * @param maxFrameLength 解码时，处理每个帧数据的最大长度 
     * @param lengthFieldOffset 该帧数据中，存放该帧数据的长度的数据的起始位置 
     * @param lengthFieldLength 记录该帧数据长度的字段本身的长度 
     * @param lengthAdjustment 修改帧数据长度字段中定义的值，可以为负数 
     * @param initialBytesToStrip 解析的时候需要跳过的字节数 
     * @param failFast 为true，当frame长度超过maxFrameLength时立即报TooLongFrameException异常，为false，读取完整个帧再报异常 
     */  
					.addLast(new LengthFieldBasedFrameDecoder(65536, 0, 4,0,0))
					.addLast(new RpcDecoder(RpcRequest.class))
					.addLast(new RpcEncoder(RpcResponse.class))
					.addLast(new ServerHandler(handlerMap));
					
					
				}
			});
			String[] array = serverAddress.split(":");
			String host = array[0];
			int port = Integer.parseInt(array[1]);
			ChannelFuture future = bootstrap.bind(host,port).sync();
			serviceRegistry.register(serverAddress);
			future.channel().closeFuture().sync();
		}
	}
	
	
	public void stop(){
		if (bossGroup != null) {
			bossGroup.shutdownGracefully();
		}
		if (workerGroup != null) {
			bossGroup.shutdownGracefully();
		}
	}
	
	public static void submit(Runnable task){
		if (threadPoolExecutor == null) {
			synchronized (Server.class) {
				if (threadPoolExecutor == null) {
					ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(65536);
					threadPoolExecutor = new ThreadPoolExecutor(16, 16, 600L, TimeUnit.SECONDS, queue);
				}
			}
		}
		threadPoolExecutor.submit(task);
	}
}
